/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: win_thread.c
 *
 * Purpose: implementation thread with windows API
 *
 * Developer:
 *   wen.gu , 2022-01-21
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "config.h"
#if defined(_MSC_VER) && (!defined(HAS_POSIX_THREAD) || (!HAS_POSIX_THREAD))
#include "avp_thread.h"

#include <windows.h>
#include <process.h>

#include "avp_log.h"


/******************************************************************************
 **    MACROS
 ******************************************************************************/
#define THD_GET_HANDLE(avp_thd) (((avp_thd) != NULL) ? ((struct _avp_thread*)avp_thd)->thd : GetCurrentThread())
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/
enum _avp_thread_state
{
    AVP_THD_READY = 0,
    AVP_THD_RUNNING,
    AVP_THD_STOP,
};

struct _avp_thread
{
    GU32 tid;
    GPTR opaque;
    avp_thread_func func;
    HANDLE thd;
};

/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/

static inline unsigned __stdcall avp_thread_runnable(void* opaque)
{
    struct _avp_thread* thd = (struct _avp_thread*)opaque;

    if ((thd->func != NULL))
    {
        thd->func(thd->opaque);
    }

    return 0;
}

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
/**
 * @brief create a thread instance with the thread func and a user opaque
 * 
 * @param func [in] the thread running function
 * @param opaque [in] a user private argument for thread function
 * @return avp_thread_t  the thread instance, if failed, return NULL
 */
avp_thread_t avp_thread_create(avp_thread_func func, GPTR opaque)
{
    if (func == NULL )
    {
        LOGE("must set a thread runnable function\n");
        return NULL;
    }

    struct _avp_thread* thd = (pthread_t*)malloc(sizoef(struct _avp_thread));
    if (thd == NULL)
    {
        LOGE("alloc thread instance failed\n");
        return NULL;
    }
    
    thd->thd = (HANDLE)_beginthreadex(NULL, 0, avp_thread_runnable, (void*)thd, CREATE_SUSPENDED, &thd->tid);

    if (thd->thd == NULL)
    {
        LOGE("create thread failed(%s)\n", strerror(errno));
        free(thd);
        return NULL;
    }

    return (avp_thread_t)thd;
}

/**
 * @brief join(wait) and destroy a thread instance
 * 
 * @param thd [in] the thread instance
 * @return void
 * @note this API maybe block forever if avp_thread_func not return
 */
void avp_thread_join(avp_thread_t thd)
{
    if (thd)
    {
        struct _avp_thread* avp_thd = (struct _avp_thread*)thd;
        DWORD ret = 0;
        WaitForSingleObject(avp_thd->thd, INFINITE);
        //GetExitCodeThread(avp_thd->thd, &ret);
        CloseHandle(avp_thd->thd);
        
        free(thd);
    }
}

/**
 * @brief start to run the given thread, which must have been initialized
 * 
 * @param thd [in] the thread instance
 * @return void 
 */
void avp_thread_run(avp_thread_t thd)
{
    if (thd)
    {
        ResumeThread(((struct _avp_thread*)thd)->thd);
    }
}

/**
 * @brief set a short name for the thread
 * 
 * @param thd  [in] the thread instance, if this arg is NULL, then is set the name for current thread which called this PAI
 * @param name [in] the name of thread
 * @return void
 */
void avp_thread_set_name(avp_thread_t thd, const GSTR name)
{
    if (name == NULL)
    {
        return ;
    }

    HANDLE pthd = THD_GET_HANDLE(thd);

    HRESULT hr = SetThreadDescription(pthd, name);
    if (FAILED(hr))
    {
        LOGE("set thread name failed(%s)\n", strerror(errno));
    }
}

/**
 * @brief get the name of the thread
 * 
 * @param thd [in] the thread instance, if this arg is NULL, then is get the name of current thread which called this API
 * @return const GPTR  the name of the thread, if failed, then return empty string ' return ""; '
 * @note   the return c string must do free after use. 
 */
const GSTR avp_thread_name(avp_thread_t thd)
{
    HANDLE pthd = THD_GET_HANDLE(thd);
    GSTR thd_name;
    HRESULT hr = GetThreadDescription(pthd, &thd_name);
    if (SUCCEEDED(hr))
    {
        return thd_name;
    }

    return "";
}


/**
 * @brief set a priority for the thread
 * 
 * @param thd      [in] the thread instance,if this arg is NULL, then is set priority for current thread which called this PAI
 * @param priority [in] the priority of thread
 * @return void
 */
void avp_thread_set_priority(avp_thread_t thd, GS32 priority)
{
    HANDLE pthd = THD_GET_HANDLE(thd);
    SetThreadPriority(pthd, priority);
}

/**
 * @brief get the priority of the thread
 * 
 * @param thd  [in] the thread instance, if this arg is NULL, then is get the priority of current thread which called this API
 * @param priority [io] the priority of thread
 * @return GErrc  success: G_OK, else error code @see GErrc
 */
GErrc avp_thread_priority(avp_thread_t thd, GS32* priority)
{
    if (priority == NULL)
    {
        return G_ErrBadParameter;
    }
    HANDLE pthd = THD_GET_HANDLE(thd);
    *priority = GetThreadPriority(pthd);

    return G_OK;
}

/**
 * @brief check the thread is current running thread
 * 
 * @param thd [in] the thread instance, if this arg is NULL, then always return GTRUE
 * @return GBOL GTRUE: the thread instance is current running thread, GFALSE: not current running thread
 */
GBOL avp_thread_is_self(avp_thread_t thd)
{
    HANDLE pthd = THD_GET_HANDLE(thd);


    return GetCurrentThreadId() == GetThreadId(pthd); /** todo refine me? return GTRUE or GFALSE by hand */
}

/**
 * @brief do sleep in milli-second unit
 * 
 * @param time_ms [in] how long to sleep
 * @return void
 */
void avp_thread_sleep(GU32 time_ms)
{
    Sleep((DWORD)time_ms);
}


#endif /** defined HAS_POSIX_THREAD */
